package ru.stepanovdn.trading.server.service.report

import jakarta.annotation.PostConstruct
import jakarta.persistence.EntityManager
import jakarta.persistence.Query
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.core.io.DefaultResourceLoader
import org.springframework.core.io.Resource
import org.springframework.data.domain.Sort
import org.springframework.stereotype.Service
import org.springframework.util.FileCopyUtils
import ru.stepanovdn.trading.server.dto.OrderReportInstrumentByFiatRow
import ru.stepanovdn.trading.server.dto.StrategyInfoReportRow
import ru.stepanovdn.trading.server.entity.Order
import ru.stepanovdn.trading.server.repository.OrderRepository
import ru.stepanovdn.trading.server.service.dictionary.InstrumentService
import ru.stepanovdn.trading.server.service.dictionary.InstrumentService.Instrument
import ru.stepanovdn.trading.server.strategy.AInstrumentByFiatStrategy
import ru.stepanovdn.trading.server.strategy.StrategySelector
import java.io.InputStreamReader
import java.math.BigDecimal
import java.math.RoundingMode
import java.util.stream.Collectors

/**
 * Service to prepare reports by strategies and orders
 */
@Service
class ReportService (
    @Autowired val orderRepository: OrderRepository,
    @Autowired val entityManager: EntityManager,
    @Autowired val strategySelector: StrategySelector,
    @Autowired val instrumentService: InstrumentService
) {
    private val log = LoggerFactory.getLogger(javaClass)
    private var reportInstrumentByFiatSql: String? = null

    fun buildReportStrategiesInfo(): List<StrategyInfoReportRow> {
        return strategySelector.allStrategies!!.stream()
            .map { s ->
                val figies: Map<String, Int> = s.figies.entries.stream()
                    .collect(Collectors.toMap({ e: Map.Entry<String?, Int?> ->
                        val instrument: Instrument? = instrumentService.getInstrument(e.key!!)
                        if (instrument == null) e.key else instrument.name
                    }, { e: Map.Entry<String?, Int?> -> e.value!! }))
                StrategyInfoReportRow(
                    isEnabled = s.isEnabled,
                    name = s.name,
                    type = s.type.title,
                    figies = figies,
                    buyCriteria = (if (s is AInstrumentByFiatStrategy) s.buyCriteria else null),
                    sellCriteria = (if (s is AInstrumentByFiatStrategy) s.sellCriteria else null),
                    history = (if (s is AInstrumentByFiatStrategy) s.historyDuration else null),
                    dropPercent = null,
                    isOnlySell = s.isOnlySell
                )
            }
            .sorted(Comparator.comparing { it.name!! })
            .collect(Collectors.toList())
    }

    val ordersSortByIdDesc: List<Order?>
        get() = orderRepository.findAll(Sort.by(Sort.Direction.DESC, "id"))

    fun buildReportInstrumentByFiat(): List<OrderReportInstrumentByFiatRow> {
        val query: Query = entityManager.createNativeQuery(reportInstrumentByFiatSql)
        return (query.resultList!! as List<Array<Any?>?>).stream()
            .map { r: Array<Any?>? ->
                OrderReportInstrumentByFiatRow(
                    figiTitle = r!![1].toString(),
                    strategyName = r[2].toString(),
                    strategyIsEnabled = strategySelector.isEnabled(r[2].toString(), r[0].toString()),
                    profitByRobot =
                    if (r[3] == null) BigDecimal.ZERO else BigDecimal(r[3].toString()).setScale(
                        2,
                        RoundingMode.HALF_UP
                    ),
                    profitByInvest = BigDecimal(r[4].toString()),
                    orders = r[5].toString().toInt(),
                    firstPrice = BigDecimal(r[6].toString()).setScale(4, RoundingMode.HALF_UP),
                    lastPrice = BigDecimal(r[7].toString()).setScale(4, RoundingMode.HALF_UP)
                )
            }
            .collect(Collectors.toList())
    }

    fun logReportInstrumentByFiat(rows: List<OrderReportInstrumentByFiatRow?>) {
        log.info("---------------------- Report instrument by fiat start ----------------------")
        for (row in rows) {
            log.info(
                "{}: {} | init price {} | last price {} | by robot {}% | by invest: {}% | orders {}",
                row!!.strategyName, row.figiTitle, row.firstPrice, row.lastPrice,
                row.profitByRobot, row.profitByInvest, row.orders
            )
        }
        log.info("---------------------- Report instrument by fiat end ------------------------\n")
    }

    @PostConstruct
    fun init() {
        val reportInstrumentByFiatSqlResource: Resource =
            DefaultResourceLoader().getResource("sql/report_instrument_by_fiat.sql")
        reportInstrumentByFiatSql =
            FileCopyUtils.copyToString(InputStreamReader(reportInstrumentByFiatSqlResource.inputStream))
    }
}
